/*!	 layergrouptreestore.cpp
**	 Template File
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2008 Chris Moore
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <synfig/general.h>

#include "trees/layergrouptreestore.h"
#include "iconcontroller.h"
#include <gtkmm/button.h>
#include <synfig/paramdesc.h>
#include <synfigapp/action.h>
#include <synfigapp/instance.h>
#include "app.h"
#include "instance.h"
#include <synfigapp/action_system.h>
#include "docks/dockmanager.h"
#include "docks/dockable.h"

#include <gtk/gtk.h>
#include <ETL/clock>
#include <gui/localization.h>

#endif

using namespace std;
using namespace etl;
using namespace synfig;
using namespace studio;

#define GROUP_NEST_CHAR	'.'

static LayerGroupTreeStore::Model& ModelHack()
{
    static LayerGroupTreeStore::Model* model(0);

    if (!model) {
        model = new LayerGroupTreeStore::Model;
    }

    return *model;
}

LayerGroupTreeStore::LayerGroupTreeStore(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface_):
    Gtk::TreeStore(ModelHack()),
    canvas_interface_(canvas_interface_)
{
    layer_icon = Gtk::Button().render_icon_pixbuf(Gtk::StockID("synfig-layer"), Gtk::ICON_SIZE_SMALL_TOOLBAR);
    group_icon = Gtk::Button().render_icon_pixbuf(Gtk::StockID("synfig-group"), Gtk::ICON_SIZE_SMALL_TOOLBAR);

    // Connect Signals to Terminals
    canvas_interface()->signal_layer_status_changed().connect(sigc::mem_fun(*this, &studio::LayerGroupTreeStore::on_layer_status_changed));
    canvas_interface()->signal_layer_new_description().connect(sigc::mem_fun(*this, &studio::LayerGroupTreeStore::on_layer_new_description));

    canvas_interface()->get_canvas()->signal_group_added().connect(sigc::hide_return(sigc::mem_fun(*this, &studio::LayerGroupTreeStore::on_group_added)));
    canvas_interface()->get_canvas()->signal_group_removed().connect(sigc::hide_return(sigc::mem_fun(*this, &studio::LayerGroupTreeStore::on_group_removed)));
    canvas_interface()->get_canvas()->signal_group_changed().connect(sigc::hide_return(sigc::mem_fun(*this, &studio::LayerGroupTreeStore::on_group_changed)));

    canvas_interface()->get_canvas()->signal_group_pair_added().connect(sigc::hide_return(sigc::mem_fun(*this, &studio::LayerGroupTreeStore::on_group_pair_added)));
    canvas_interface()->get_canvas()->signal_group_pair_removed().connect(sigc::hide_return(sigc::mem_fun(*this, &studio::LayerGroupTreeStore::on_group_pair_removed)));

    rebuild();
}

LayerGroupTreeStore::~LayerGroupTreeStore()
{

    if (getenv("SYNFIG_DEBUG_DESTRUCTORS")) {
        synfig::info("LayerGroupTreeStore::~LayerGroupTreeStore(): Deleted");
    }
}

bool
LayerGroupTreeStore::search_func(const Glib::RefPtr<TreeModel>&, int, const Glib::ustring& x, const TreeModel::iterator& iter)
{
    const Model model;

    Glib::ustring substr(x.uppercase());
    Glib::ustring label((*iter)[model.label]);
    label = label.uppercase();

    return label.find(substr) == Glib::ustring::npos;
}

Glib::RefPtr<LayerGroupTreeStore>
LayerGroupTreeStore::create(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface_)
{
    return Glib::RefPtr<LayerGroupTreeStore>(new LayerGroupTreeStore(canvas_interface_));
}

void
LayerGroupTreeStore::get_value_vfunc(const Gtk::TreeModel::iterator& iter, int column, Glib::ValueBase& value)const
{
    if (column == model.child_layers.index()) {
        Glib::Value<LayerList> x;
        g_value_init(x.gobj(), x.value_type());

        if ((bool)(*iter)[model.is_group]) {
            set<Layer::Handle> layer_set(canvas_interface()->get_canvas()->get_layers_in_group((Glib::ustring)(*iter)[model.group_name]));

            x.set(LayerList(layer_set.begin(), layer_set.end()));
        } else if ((bool)(*iter)[model.is_layer]) {
            LayerList layer_list;
            layer_list.push_back((Layer::Handle)(*iter)[model.layer]);
            x.set(layer_list);
        }

        g_value_init(value.gobj(), x.value_type());
        value = x;
    } else if (column == model.all_layers.index()) {
        Glib::Value<LayerList> x;
        g_value_init(x.gobj(), x.value_type());

        if ((bool)(*iter)[model.is_group]) {
            LayerList layer_list;
            Gtk::TreeModel::iterator child_iter(iter->children().begin());

            for (; child_iter; ++child_iter) {
                LayerList layer_list2((LayerList)(*child_iter)[model.all_layers]);

                for (; layer_list2.size(); layer_list2.pop_front()) {
                    layer_list.push_back(layer_list2.front());
                }
            }

            x.set(layer_list);
        } else if ((bool)(*iter)[model.is_layer]) {
            LayerList layer_list;
            layer_list.push_back((Layer::Handle)(*iter)[model.layer]);
            x.set(layer_list);
        }

        g_value_init(value.gobj(), x.value_type());
        value = x;
    } else if (column == model.group_name.index()) {
        if ((bool)(*iter)[model.is_group]) {
            return Gtk::TreeStore::get_value_vfunc(iter, column, value);
        }

        return get_value_vfunc(iter->parent(), column, value);
    } else if (column == model.parent_group_name.index()) {
        if (iter->parent()) {
            return get_value_vfunc(iter->parent(), model.group_name.index(), value);
        }

        Glib::Value<Glib::ustring> x;
        g_value_init(x.gobj(), x.value_type());
        x.set(Glib::ustring());
        g_value_init(value.gobj(), x.value_type());
        value = x;
    } else if (column == model.label.index()) {
        if ((bool)(*iter)[model.is_group]) {
            Glib::Value<Glib::ustring> x;
            g_value_init(x.gobj(), x.value_type());

            Glib::ustring group_name((*iter)[model.group_name]);

            // Get rid of any parent group crap
            while (group_name.find(GROUP_NEST_CHAR) != Glib::ustring::npos) {
                group_name = Glib::ustring(group_name, group_name.find(GROUP_NEST_CHAR) + 1, Glib::ustring::npos);
            }

            x.set(group_name);

            g_value_init(value.gobj(), x.value_type());

            value = x;
        } else if ((bool)(*iter)[model.is_layer]) {
            synfig::Layer::Handle layer((*iter)[model.layer]);

            if (!layer) {
                return;
            }

            Glib::Value<Glib::ustring> x;
            g_value_init(x.gobj(), x.value_type());

            x.set(layer->get_non_empty_description());

            g_value_init(value.gobj(), x.value_type());
            value = x;
        }
    } else if (column == model.tooltip.index()) {
        synfig::Layer::Handle layer((*iter)[model.layer]);

        if (!layer) {
            return;
        }

        Glib::Value<Glib::ustring> x;
        g_value_init(x.gobj(), x.value_type());

        x.set(layer->get_local_name());

        g_value_init(value.gobj(), x.value_type());
        value = x;
    } else if (column == model.canvas.index()) {
        synfig::Layer::Handle layer((*iter)[model.layer]);

        if (!layer) {
            return;
        }

        Glib::Value<Canvas::Handle> x;
        g_value_init(x.gobj(), x.value_type());

        x.set(layer->get_canvas());

        g_value_init(value.gobj(), x.value_type());
        value = x;
    } else if (column == model.active.index()) {
        Glib::Value<bool> x;
        g_value_init(x.gobj(), x.value_type());

        if ((bool)(*iter)[model.is_layer]) {
            synfig::Layer::Handle layer((*iter)[model.layer]);
            x.set(layer->active());
        } else if ((bool)(*iter)[model.is_group]) {
            int activecount(0), total(0);
            Gtk::TreeModel::iterator child_iter(iter->children().begin());

            for (; child_iter; ++child_iter) {
                total++;

                if ((*child_iter)[model.active]) {
                    activecount++;
                }
            }

            x.set(activecount > 0);
        } else {
            x.set(false);
        }

        g_value_init(value.gobj(), x.value_type());
        g_value_copy(x.gobj(), value.gobj());
    } else if (column == model.icon.index()) {
        Glib::Value<Glib::RefPtr<Gdk::Pixbuf> > x;
        g_value_init(x.gobj(), x.value_type());

        if ((bool)(*iter)[model.is_layer]) {
            synfig::Layer::Handle layer((*iter)[model.layer]);

            if (!layer) {
                return;
            }

            x.set(get_tree_pixbuf_layer(layer->get_name()));
        }

        if ((bool)(*iter)[model.is_group]) {
            x.set(group_icon);
        }

        g_value_init(value.gobj(), x.value_type());
        g_value_copy(x.gobj(), value.gobj());
    } else {
        Gtk::TreeStore::get_value_vfunc(iter, column, value);
    }
}

void
LayerGroupTreeStore::set_value_impl(const Gtk::TreeModel::iterator& iter, int column, const Glib::ValueBase& value)
{

    if (column >= get_n_columns_vfunc()) {
        g_warning("LayerGroupTreeStore::set_value_impl: Bad column (%d)", column);
        return;
    }

    if (!g_value_type_compatible(G_VALUE_TYPE(value.gobj()), get_column_type_vfunc(column))) {
        g_warning("LayerGroupTreeStore::set_value_impl: Bad value type");
        return;
    }

    try {
        if (column == model.label.index()) {
            Glib::Value<Glib::ustring> x;
            g_value_init(x.gobj(), model.label.type());
            g_value_copy(value.gobj(), x.gobj());

            if ((bool)(*iter)[model.is_layer]) {
                synfig::Layer::Handle layer((*iter)[model.layer]);

                if (!layer) {
                    return;
                }

                synfig::String new_desc(x.get());

                if (new_desc == layer->get_local_name()) {
                    new_desc = synfig::String();
                }

                if (new_desc == layer->get_description()) {
                    return;
                }

                synfigapp::Action::Handle action(synfigapp::Action::create("LayerSetDesc"));

                if (!action) {
                    return;
                }

                action->set_param("canvas", canvas_interface()->get_canvas());
                action->set_param("canvas_interface", canvas_interface());
                action->set_param("layer", layer);
                action->set_param("new_description", synfig::String(x.get()));

                canvas_interface()->get_instance()->perform_action(action);
                return;
            } else if ((bool)(*iter)[model.is_group]) {
                synfig::String group((Glib::ustring)(*iter)[model.label]);
                synfig::String new_group(x.get());

                if (x.get() == group) {
                    return;
                }

                Glib::ustring group_name((*iter)[model.group_name]);
                group = group_name;
                new_group.clear();

                // Get rid of any parent group crap
                while (group_name.find(GROUP_NEST_CHAR) != Glib::ustring::npos) {
                    new_group += Glib::ustring(group_name, 0, group_name.find(GROUP_NEST_CHAR) + 1);
                    group_name = Glib::ustring(group_name, group_name.find(GROUP_NEST_CHAR) + 1, Glib::ustring::npos);
                }

                new_group += x.get();

                // Check to see if this group is real or not.
                // If it isn't real, then renaming it is a cinch.
                // We know it isn't real if it doesn't have any
                // children yet.
                if (iter->children().empty()) {
                    (*iter)[model.group_name] = new_group;
                } else {
                    synfigapp::Action::Handle action(synfigapp::Action::create("GroupRename"));

                    if (!action) {
                        return;
                    }

                    action->set_param("canvas", canvas_interface()->get_canvas());
                    action->set_param("canvas_interface", canvas_interface());
                    action->set_param("group", group);
                    action->set_param("new_group", new_group);

                    canvas_interface()->get_instance()->perform_action(action);
                }

                return;
            }

            return;
        } else if (column == model.active.index()) {
            Glib::Value<bool> x;
            g_value_init(x.gobj(), model.active.type());
            g_value_copy(value.gobj(), x.gobj());

            if ((bool)(*iter)[model.is_layer]) {
                synfig::Layer::Handle layer((*iter)[model.layer]);

                if (!layer) {
                    return;
                }

                synfigapp::Action::Handle action(synfigapp::Action::create("LayerActivate"));

                if (!action) {
                    return;
                }

                action->set_param("canvas", canvas_interface()->get_canvas());
                action->set_param("canvas_interface", canvas_interface());
                action->set_param("layer", layer);
                action->set_param("new_status", bool(x.get()));

                canvas_interface()->get_instance()->perform_action(action);
                return;
            } else if (!iter->children().empty()) {
                synfigapp::Action::PassiveGrouper group(
                    get_canvas_interface()->get_instance().get(),
                    String(
                        x.get() ? _("Activate ") : _("Deactivate ")
                    ) + (Glib::ustring)(*iter)[model.label]
                );

                Gtk::TreeModel::iterator child_iter(iter->children().begin());

                for (; child_iter; ++child_iter) {
                    (*child_iter)[model.active] = x.get();
                }

                Gtk::TreeStore::set_value_impl(iter, column, value);
            }
        } else {
            Gtk::TreeStore::set_value_impl(iter, column, value);
        }

    } catch (std::exception x) {
        g_warning("%s", x.what());
    }
}

bool
LayerGroupTreeStore::row_draggable_vfunc(const TreeModel::Path& /*path*/)const
{

    return true;
}

bool
LayerGroupTreeStore::drag_data_get_vfunc(const TreeModel::Path& path, Gtk::SelectionData& selection_data)const
{
    if (!const_cast<LayerGroupTreeStore*>(this)->get_iter(path)) {
        return false;
    }

    Gtk::TreeModel::Row row(*const_cast<LayerGroupTreeStore*>(this)->get_iter(path));

    if ((bool)row[model.is_layer]) {
        Layer* layer(((Layer::Handle)row[model.layer]).get());
        assert(layer);

        std::vector<Layer*> layers;

        layers.push_back(layer);

        selection_data.set("LAYER", 8, reinterpret_cast<const guchar*>(&layers.front()), sizeof(void*)*layers.size());

        return true;
    } else if ((bool)row[model.is_group]) {
        synfig::String group((Glib::ustring)row[model.group_name]);

        if (group.empty()) {
            return false;
        }

        selection_data.set("GROUP", 8, reinterpret_cast<const guchar*>(&*group.begin()), sizeof(void*)*group.size());

        return true;
    }

    return false;
}

bool
LayerGroupTreeStore::drag_data_delete_vfunc(const TreeModel::Path& /*path*/)
{
    return true;
}

bool
LayerGroupTreeStore::row_drop_possible_vfunc(const TreeModel::Path& dest, const Gtk::SelectionData& selection_data)const
{
    Gtk::TreeIter iter(const_cast<LayerGroupTreeStore*>(this)->get_iter(dest));

    if (!iter) {
        return false;
    }

    if (synfig::String(selection_data.get_data_type()) == "LAYER") {
        return true;
    }

    if (synfig::String(selection_data.get_data_type()) == "GROUP") {
        synfig::String dest_group((Glib::ustring)(*iter)[model.group_name]);
        synfig::String src_group(reinterpret_cast<const gchar*>(selection_data.get_data()));

        // Avoid putting a group inside of itself
        if (dest_group.size() > src_group.size() && src_group == String(dest_group, 0, src_group.size())) {
            return false;
        }

        return true;
    }

    return false;



    return false;
}

bool
LayerGroupTreeStore::drag_data_received_vfunc(const TreeModel::Path& dest, const Gtk::SelectionData& selection_data)
{
    if (!get_iter(dest)) {
        return false;
    }

//	bool ret=false;

    Gtk::TreeModel::Row row(*get_iter(dest));

    synfigapp::Action::PassiveGrouper passive_grouper(canvas_interface()->get_instance().get(), _("Reset"));

    if ((selection_data.get_length() >= 0) && (selection_data.get_format() == 8)) {
        synfig::String dest_group;

        dest_group = (Glib::ustring)row[model.group_name];

        if (dest_group.empty()) {
            return false;
        }

        if (synfig::String(selection_data.get_data_type()) == "LAYER") {
            synfigapp::Action::Handle action(synfigapp::Action::create("GroupAddLayers"));

            if (!action) {
                return false;
            }

            action->set_param("canvas", canvas_interface()->get_canvas());
            action->set_param("canvas_interface", canvas_interface());
            action->set_param("group", dest_group);

            for (unsigned int i = 0; i < selection_data.get_length() / sizeof(void*); i++) {
                Layer::Handle layer(reinterpret_cast<Layer**>(const_cast<guint8*>(selection_data.get_data()))[i]);
                assert(layer);

                action->set_param("layer", layer);
            }

            if (!canvas_interface()->get_instance()->perform_action(action)) {
                passive_grouper.cancel();
                return false;
            }

            return true;
        }

        if (synfig::String(selection_data.get_data_type()) == "GROUP") {
            synfig::String src_group(reinterpret_cast<const gchar*>(selection_data.get_data()));
            synfig::String group(src_group);

            // Get rid of any parent group crap
            while (group.find(GROUP_NEST_CHAR) != Glib::ustring::npos) {
                group = Glib::ustring(group, group.find(GROUP_NEST_CHAR) + 1, Glib::ustring::npos);
            }

            group = dest_group + GROUP_NEST_CHAR + group;

            synfigapp::Action::Handle action(synfigapp::Action::create("GroupRename"));

            if (!action) {
                return false;
            }

            action->set_param("canvas", canvas_interface()->get_canvas());
            action->set_param("canvas_interface", canvas_interface());
            action->set_param("group", src_group);
            action->set_param("new_group", group);

            if (!canvas_interface()->get_instance()->perform_action(action)) {
                passive_grouper.cancel();
                return false;
            }

            return true;
        }
    }

    return false;
}

void
LayerGroupTreeStore::rebuild()
{
    rebuilding = true;

    // etl::clock timer;timer.reset();
    try {

        // Clear out the current list
        clear();
        Canvas::Handle canvas(canvas_interface()->get_canvas());
        std::set<String> groups(canvas->get_groups());

        for (; groups.size(); groups.erase(groups.begin())) {
            String group(*groups.begin());
            Gtk::TreeRow row(on_group_added(group));
            std::set<Layer::Handle> layers(canvas->get_layers_in_group(group));

            for (; layers.size(); layers.erase(layers.begin())) {
                Gtk::TreeRow layer_row(*(prepend(row.children())));
                Layer::Handle layer(*layers.begin());
                set_row_layer(layer_row, layer);
            }
        }

        // Go ahead and add all the layers

    } catch (...) {
        rebuilding = false;
        throw;
    }

    rebuilding = false;
}

void
LayerGroupTreeStore::refresh()
{
    rebuild();
}

void
LayerGroupTreeStore::refresh_row(Gtk::TreeModel::Row &row)
{
    if ((bool)row[model.is_layer]) {
        Layer::Handle layer = row[model.layer];

        //	row[model.z_depth]=Time::begin();
    }

    Gtk::TreeModel::Children children = row.children();
    Gtk::TreeModel::Children::iterator iter;

    if (!children.empty())
        for (iter = children.begin(); iter && iter != children.end(); ++iter) {
            Gtk::TreeRow row = *iter;
            refresh_row(row);
        }
}

void
LayerGroupTreeStore::set_row_layer(Gtk::TreeRow &row, synfig::Layer::Handle &handle)
{
    row[model.is_layer] = true;
    row[model.is_group] = false;
    row[model.layer] = handle;
}

Gtk::TreeRow
LayerGroupTreeStore::on_group_added(synfig::String group)
{
    // Check to see if this group perhaps already
    // exists
    {
        Gtk::TreeModel::Children::iterator iter;

        if (find_group_row(group,  iter)) {
            return *iter;
        }
    }

    if (group.find(GROUP_NEST_CHAR) != String::npos) {
        Gtk::TreeModel::Children::iterator iter;
        String parent_name;

        do {
            if (parent_name.size()) {
                parent_name += GROUP_NEST_CHAR;
            }

            parent_name += string(group, 0, group.find(GROUP_NEST_CHAR));

            if (!find_group_row(parent_name, iter)) {
                iter = on_group_added(parent_name);
            }

            group = String(group, group.find(GROUP_NEST_CHAR) + 1, String::npos);
        } while (group.find(GROUP_NEST_CHAR) != String::npos);

        if (parent_name.size()) {
            parent_name += GROUP_NEST_CHAR;
        }

        parent_name += group;

        if (iter) {
            Gtk::TreeRow row(*(prepend(iter->children())));
            row[model.group_name] = parent_name;
            row[model.is_layer] = false;
            row[model.is_group] = true;
            on_activity();
            return row;
        }
    }

    Gtk::TreeRow row(*(append()));
    row[model.group_name] = group;
    row[model.is_layer] = false;
    row[model.is_group] = true;
    on_activity();
    return row;
}

bool
LayerGroupTreeStore::on_group_removed(synfig::String group)
{
    Gtk::TreeModel::Children::iterator iter;

    if (find_group_row(group, iter) && iter->children().size() == 0) {
        erase(iter);
    } else {
        return false;
    }

    return true;
}

bool
LayerGroupTreeStore::on_group_changed(synfig::String /*group*/)
{
    return true;
}

void
LayerGroupTreeStore::on_group_pair_added(synfig::String group, etl::handle<synfig::Layer> layer)
{
    if (!layer->get_canvas()) {
        return;
    }

    Gtk::TreeModel::Children::iterator iter;

    if (!find_group_row(group, iter)) {
        iter = on_group_added(group);
    }

    Gtk::TreeRow layer_row(*(append(iter->children())));
    set_row_layer(layer_row, layer);
    on_activity();
}

void
LayerGroupTreeStore::on_group_pair_removed(synfig::String group, etl::handle<synfig::Layer> layer)
{
    if (!layer->get_canvas()) {
        return;
    }

    Gtk::TreeModel::Children::iterator iter;

    if (!find_group_row(group, iter)) {
        return;
    }

    Gtk::TreeModel::Children::iterator prev, layer_iter;

    if (!find_layer_row_(layer, layer->get_canvas(), iter->children(), layer_iter, prev)) {
        return;
    }

    erase(layer_iter);

    on_activity();
}

void
LayerGroupTreeStore::on_activity()
{
    // If we aren't rebuilding and the last action
    // had something to do with groups, then go
    // ahead and present the groups dialog.
    if (!rebuilding && canvas_interface()->get_instance()->get_most_recent_action_name().find("Group") != String::npos)
        try {
            App::dock_manager->find_dockable("groups").present();
        } catch (...) { }
}

void
LayerGroupTreeStore::on_layer_status_changed(synfig::Layer::Handle handle, bool /*x*/)
{
    Gtk::TreeModel::Children::iterator iter;

    if (find_layer_row(handle, iter)) {
        (*iter)[model.layer] = handle;
    } else {
        // Not need to send a warning when a layer changes its status and
        // it is not found in any group.
        // synfig::warning("Couldn't find layer to be activated in layer list. Rebuilding index...");
        rebuild();
    }
}

void
LayerGroupTreeStore::on_layer_new_description(synfig::Layer::Handle handle, synfig::String desc)
{
    Gtk::TreeModel::Children::iterator iter;

    if (find_layer_row(handle, iter)) {
        Gtk::TreeRow row(*iter);

        Layer::Handle layer(row[model.layer]);

        if (desc.empty()) {
            // row[model.label]=layer->get_local_name();
            row[model.tooltip] = Glib::ustring(_("Layer"));
        } else
            // row[model.label]=layer->get_description();
        {
            row[model.tooltip] = layer->get_local_name();
        }
    } else {
        rebuild();
    }
}

bool
LayerGroupTreeStore::find_layer_row_(const synfig::Layer::Handle &layer, synfig::Canvas::Handle canvas, Gtk::TreeModel::Children layers, Gtk::TreeModel::Children::iterator &iter, Gtk::TreeModel::Children::iterator &prev)
{
    assert(layer);

    {
        for (iter = prev = layers.begin(); iter && iter != layers.end(); prev = iter++) {
            Gtk::TreeModel::Row row = *iter;

            if ((bool)row[model.is_layer] && layer == (synfig::Layer::Handle)row[model.layer]) {
                return true;
            }
        }

        iter = children().end();
    }

    Gtk::TreeModel::Children::iterator iter2;

    for (iter2 = layers.begin(); iter2 && iter2 != layers.end(); ++iter2) {
        Gtk::TreeModel::Row row = *iter2;
        assert((bool)true);

        if (row.children().empty()) {
            continue;
        }



        if (find_layer_row_(layer, canvas, iter2->children(), iter, prev)) {
            return true;
        }
    }

    iter = children().end();
    return false;
}

bool
LayerGroupTreeStore::find_layer_row(const synfig::Layer::Handle &layer, Gtk::TreeModel::Children::iterator &iter)
{
    Gtk::TreeModel::Children::iterator prev;
    return find_layer_row_(layer, canvas_interface()->get_canvas(), children(), iter, prev);
}

bool
LayerGroupTreeStore::find_group_row(const synfig::String &group, Gtk::TreeModel::Children::iterator &iter)
{
    Gtk::TreeModel::Children::iterator prev;
    return find_group_row_(group, children(), iter, prev);
}

bool
LayerGroupTreeStore::find_group_row_(const synfig::String &group, Gtk::TreeModel::Children layers, Gtk::TreeModel::Children::iterator &iter, Gtk::TreeModel::Children::iterator &prev)
{
    {
        for (iter = prev = layers.begin(); iter && iter != layers.end(); prev = iter++) {
            Gtk::TreeModel::Row row = *iter;

            if ((bool)row[model.is_group] && group == (Glib::ustring)row[model.group_name]) {
                return true;
            }
        }

        iter = children().end();
    }

    Gtk::TreeModel::Children::iterator iter2;

    for (iter2 = layers.begin(); iter2 && iter2 != layers.end(); ++iter2) {
        Gtk::TreeModel::Row row = *iter2;
        assert((bool)true);

        if (row.children().empty()) {
            continue;
        }

        if (find_group_row_(group, iter2->children(), iter, prev)) {
            return true;
        }
    }

    iter = children().end();
    return false;
}

bool
LayerGroupTreeStore::find_prev_layer_row(const synfig::Layer::Handle &layer, Gtk::TreeModel::Children::iterator &prev)
{
    Gtk::TreeModel::Children::iterator iter;

    if (!find_layer_row_(layer, canvas_interface()->get_canvas(), children(), iter, prev)) {
        return false;
    }

    if (iter == children().begin()) {
        return false;
    }

    return true;
}